#include "QueryPlan.h"
#include "QueryExecutor.h"
#include <sys/time.h>

using namespace std;

extern "C" {
	int yyparse(void);   // defined in y.tab.c
	struct YY_BUFFER_STATE *yy_scan_string(const char*);
}

typedef enum {create, insert, sel, setOutput, drop, update, setJoin} fileType;

extern struct NameList *iTableName;
extern struct AttributeList *tableAtts;
extern struct NameList *sortAtts;
extern int tableType;
extern struct NameList *insertFileName;
extern struct NameList *outFileName;
extern int queryType;
extern int outputMode;
extern int exitPrompt;

int main () {
    struct timeval _start, _end;
    cout << endl << "SQL> ";

//        cin >> i;
//        if(i == 1)
//            cnf = "SELECT p.ps_partkey FROM partsupp AS p WHERE (p.ps_supplycost < 1.04)";
//        else if(i == 2)
//            cnf = "SELECT p.p_partkey, p.p_name, p.p_retailprice FROM part AS p WHERE (p.p_retailprice > 931.00) AND (p.p_retailprice < 931.4)";
//        else if(i == 3)
//            cnf = "SELECT SUM(s.s_acctbal + (s.s_acctbal * 1.05)) FROM supplier AS s WHERE (1 = 1)";
//        else if(i == 4)
//            cnf = "SELECT SUM (p.ps_supplycost) FROM supplier AS s, partsupp AS p WHERE (s.s_suppkey = p.ps_suppkey)";
//        else if(i == 5)
//            cnf = "SELECT DISTINCT p.ps_suppkey FROM partsupp AS p WHERE (p.ps_supplycost < 100.11)";
//        else if(i == 6)
//            cnf = "SELECT SUM (p.ps_supplycost) FROM supplier AS s, partsupp AS p WHERE (s.s_suppkey = p.ps_suppkey) GROUP BY s.s_nationkey"; //??8
//        else if(i == 7)
//            cnf = "SELECT SUM(ps.ps_supplycost) FROM part AS p, supplier AS s, partsupp AS ps WHERE (p.p_partkey = ps.ps_partkey) AND (s.s_suppkey = ps.ps_suppkey) AND (s.s_acctbal > 2500.0)";
//        else if(i == 8)
//            cnf = "SELECT l.l_orderkey, l.l_partkey, l.l_suppkey FROM lineitem AS l WHERE (l.l_returnflag = 'R') AND (l.l_discount < 0.05 OR l.l_returnflag = 'R') AND (l.l_shipmode = 'MAIL')";
//        else if(i == 9)
//            cnf = "SELECT p.p_partkey, l.l_partkey, p.p_container, l.l_shipinstruct, l.l_shipmode FROM part AS p, lineitem AS l WHERE (l.l_partkey = p.p_partkey) AND (l.l_shipmode = 'AIR' OR l.l_shipmode = 'AIR REG') AND (p.p_container ='SM BOX' OR p.p_container = 'SM PACK') AND (l.l_shipinstruct = 'DELIVER IN PERSON')";
//        else if(i == 10)
//            cnf = "SELECT n.n_name, r.r_name FROM region AS r, nation AS n WHERE (r.r_regionkey = n.n_regionkey) AND (r.r_name = 'AMERICA')";
//        else if(i == 11)
//            cnf = "SELECT SUM(l.l_quantity) FROM lineitem AS l, orders AS o, customer AS c WHERE (o.o_totalprice < 1000.0) AND (l.l_orderkey = o.o_orderkey) AND (c.c_custkey = o.o_custkey) AND (c.c_custkey < 100)";
//        else if(i == 12)
//            cnf = "SELECT SUM(l.l_quantity) FROM lineitem AS l, orders AS o WHERE (o.o_totalprice < 0.0) AND (l.l_quantity < 0.0) AND (l.l_orderkey = o.o_orderkey)";
//        else if(i == 13)
//            cnf = "SELECT s.s_acctbal FROM supplier AS s WHERE (s.s_acctbal = 1.0)";
//        else if(i == 14)
//            cnf = "CREATE TABLE test (id INTEGER, name DOUBLE, comment STRING) AS HEAP";
//        else if(i == 15)
//            cnf = "INSERT 'test' INTO test";
//        else if(i == 16)
//            cnf = "SELECT t.id FROM test AS t WHERE (1 = 1)";
//        else if(i == 17)
//            cnf = "DROP TABLE test";
//        else if(i == 18)
//            cnf = "SET OUTPUT STDOUT";
//        else if(i == 19)
//            cnf = "SET OUTPUT NONE";

//SELECT SUM(l.l_quantity) FROM lineitem AS l, orders AS o, customer AS c, supplier AS s, part AS p, nation AS n, region AS r
//WHERE (l.l_orderkey = o.o_orderkey) AND (l.l_partkey = p.p_partkey) AND (o.o_custkey = c.c_custkey)
//AND (l.l_suppkey = s.s_suppkey) AND (s.s_nationkey = n.n_nationkey) AND (n.n_regionkey = r.r_regionkey)

//SELECT r.r_name FROM region AS r WHERE (1 = 1)


//        else if (i == 0)
//            cnf = "EXIT";
//
//        yy_scan_string(cnf);
    yyparse();

    if(exitPrompt == 1){
        exit(1);
    }

    gettimeofday(&_start, NULL);

    fstream file;
    bool flag = false;
    string line, oFile, oMode;
    file.open("output");
    int outMode = 1;
    if (file.is_open()) {
        getline(file, line);
        oMode = line.substr(0,line.find(" "));
        oFile = line.substr(line.find(" ")+1);
        outMode = atoi((char*) oMode.c_str());
    }


    char* tblName;
    if(queryType == create){
        vector<Attribute> tabAtts;
        vector<string> sortOrder;
        QueryExecutor *qxe = new QueryExecutor();

        if (iTableName != NULL){
            tblName = iTableName->name;
        }
        else {
            cerr << "\nError - No Table Name!"<<endl;
            exit (1);
        }


        if (tableAtts != NULL){
            while(tableAtts != NULL){
                Attribute att_pair;
                att_pair.name = tableAtts->attrName;
                switch (tableAtts->code){
                    case 1:
                    att_pair.myType = Double;
                    break;

                    case 2:
                    att_pair.myType = Int;
                    break;

                    case 4:
                    att_pair.myType = String;
                    break;
                }
                tabAtts.push_back(att_pair);
                tableAtts = tableAtts->next;
            }
        }
        else {
            cerr << "\nError - Attribute list is Empty!";
            exit(1);
        }

        switch (tableType){
            //HEAP
            case 0:
                qxe->CreateTable(tblName, tabAtts, false, sortOrder);
                break;

            //SORTED
            case 1:
            if (sortAtts != NULL){
                while (sortAtts != NULL) {
                    sortOrder.push_back(sortAtts->name);
                    sortAtts = sortAtts->next;
                }
                qxe->CreateTable(tblName, tabAtts, true, sortOrder);
            }
            else{
                cerr << "\nError - No Sort Attributes!"<<endl;
                exit(1);
            }
            break;

            default:
                qxe->CreateTable(tblName, tabAtts, false, sortOrder);
            break;

        }

    }

    else if (queryType == insert){
        QueryExecutor *qxe = new QueryExecutor();
        if (iTableName != NULL && insertFileName != NULL) {
            char *inFileName;
            tblName = iTableName->name;
            inFileName = insertFileName->name;

            qxe->InsertIntoTable(tblName, inFileName);
        }
    }

    else if (queryType == sel){
        QueryPlan *obj = new QueryPlan(1);
        QueryPlanNode *root = obj->GetOptimalPlan();
        if (root == NULL)
            cout << "No Join tree \n";
        else{
            if (outMode == 1 || outMode == 0)
                obj->Print(root);

            if (outMode == 1){
                obj->ExecuteTree(root);
                //obj->WaitUntilDone(root);

                Record rec;
                int count=0;
                if(root->out_pipe != NULL){
                    while (root->out_pipe->Remove(&rec)) {
                        rec.Print(root->node_out_sch);
                        count++;
                    }
                }
                cout<<"\n Record Count : "<<count<<endl;
            }
            obj->Clean(root);
        }
    }

    else if (queryType == setOutput){
        QueryExecutor *qxe = new QueryExecutor();
        char* outputFile = NULL;
        if(outFileName != NULL){
            outputFile = outFileName->name;
        }
        qxe->SetOutput(outputMode, outputFile);
    }

    else if (queryType == setJoin){
        QueryExecutor *qxe = new QueryExecutor();
        qxe->SetJoin(outputMode);
    }

    else if (queryType == drop){
        QueryExecutor *qxe = new QueryExecutor();
        if (iTableName != NULL) {
            tblName = iTableName->name;
            qxe->DropTable(tblName);
        }
    }

    gettimeofday(&_end, NULL);

    long mtime, seconds, useconds;
    seconds  = _end.tv_sec  - _start.tv_sec;
    useconds = _end.tv_usec - _start.tv_usec;

    mtime = ((seconds) * 1000 + useconds/1000.0) + 0.5;
    seconds = mtime / 1000;

    cout << endl <<"Elapsed time: " << seconds <<" seconds" << endl;

    if (queryType == sel)
            exit(1);
}
